#
# RCON Anywhere - http://www.rcon-anywhere.net
# Desktop tool
#
# Created by Lucas van Dijk (www.return1.net)
#
# Package serverstats
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
#
# $Id: sockets.py 79 2009-06-06 13:36:20Z luckyluke56 $
#

import socket
import select

from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, \
	 ENOTCONN, ESHUTDOWN, EINTR, EISCONN, errorcode

class BaseConnection(object):
	"""
		Represents a connection to some host
	"""

	def __init__(self, type):
		self._type = type

	def open(self, addr, timeout=5):
		raise NotImplementedError

	def close(self):
		raise NotImplementedError

	def send(self, data):
		raise NotImplementedError

	def recv(self, length):
		raise NotImplementedError

class Connection(BaseConnection):
	"""
		A simple synchronous connection to some host
	"""

	def open(self, addr, timeout=5):
		"""
			Opens and creates a new socket object plus an IOChannel
			object, connecting to the given address

			@type addr: tuple
			@param addr: A tuple containing the address and port
		"""

		for res in socket.getaddrinfo(addr[0], addr[1], socket.AF_UNSPEC, self._type):
			af, socktype, proto, canonname, sa = res
			try:
				self._socket = socket.socket(af, socktype, proto)
			except socket.error, msg:
				self._socket = None
				continue
			try:
				self._socket.connect(sa)
			except socket.error, msg:
				self._socket.close()
				self._socket = None
				continue
			break

		if self._socket == None:
			raise socket.error((-1, "Could not open socket"))
		
		self._timeout = timeout
		self._socket.settimeout(timeout)

		self.connected = True

	def close(self):
		"""
			Closes the socket
		"""

		try:
			self._socket.close()
			del self._socket
			self._fileno = -1
			self.connected = False
		except socket.error, e:
			if e[0] in [ECONNRESET, ENOTCONN]:
				pass
			else:
				raise
		except AttributeError:
			pass

	def recv(self, length):
		"""
			Reads the number of bytes from the socket given by length

			@type length: int
			@param length: The number of bytes to read

			@rtype: string
			@return: The data received
		"""
		
		# Check if readable
		(readable, writeable, ex) = select.select([self._socket], [], [], self._timeout)
		
		if not readable:
			raise socket.error((-1, 'Not connected to host'))
		
		data = self._socket.recv(length)

		if data == "":
			raise socket.error((-1, 'Not connected to host'))

		return data

	def send(self, data):
		"""
			Sends data to the socket

			@type data: string
			@param data: The data to send
		"""

		self._socket.send(data)

	@property
	def socket(self):
		return self._socket

	@property
	def fileno(self):
		return self._fileno

	def __getattr__(self, name):
		"""
			Redirect calls to our socket object

			@type name: string
			@param name: Attribute name
		"""

		if hasattr(self, '_socket') and hasattr(self._socket, name):
			return getattr(self._socket, name)
		else:
			raise AttributeError, 'Undefined attribute %s' % name
